package com.zty.user.init;

import com.alibaba.fastjson.JSON;
import com.zty.user.designPattern.builderPattern.demo.SkuDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;


/**
 * @Author ZTY
 * @Description 服务预热
 * 服务预热是一种常见的技术手段，用于在将服务对外提供之前，提前将服务运行至一个"热身"状态，以便服务可以更快地响应外部请求。
 * 这个概念类似于运动员在比赛前的热身，目的是为了让身体达到一个较好的状态，以便能够有更好的表现。
 * 在计算机服务中，预热可以通过多种方式实现，例如，加载必要的数据到缓存中、初始化JVM中的类、编译优化热点代码等。
 *
 * 服务预热的主要作用包括：
 *
 * 1. **提高响应速度**：
 * 通过预热，服务可以在处理第一个请求时就快速响应，而不需要在第一次请求时才开始加载资源，这对于高性能的服务来说非常重要。
 *
 * 2. **JVM优化**：
 * 对于Java应用，JVM在启动时并不会立即进行所有的优化，很多优化需要在应用运行一段时间后，
 * 当JVM监测到热点代码时才会进行。预热可以帮助JVM更早地识别热点代码并进行即时编译优化。
 *
 * 3. **减少延迟**：
 * 某些资源加载、初始化或者JIT编译可能会造成延迟。预热可以将这些操作提前完成，减少实际服务中的延迟。
 *
 * 4. **数据库连接**：
 * 预热可以用于初始化数据库连接池，确保在处理请求时连接已经可用，避免了建立连接的开销。
 *
 *
 * 一般服务预热用于以下场景：
 *
 * - **高并发服务**：
 * 对于高并发的在线服务，比如电商平台、支付系统等，服务预热可以确保在高流量到来之前服务已经准备好，能够快速响应。
 *
 * - **大型分布式应用**：
 * 在大型分布式系统中，服务的启动可能会涉及到复杂的依赖和资源准备，预热可以保证服务在对外提供服务前达到稳定状态。
 *
 * - **微服务架构**：
 * 在微服务架构中，服务之间可能会有依赖关系，预热可以确保服务在开始处理依赖请求之前已经是可用状态。
 *
 * - **重启或部署**：
 * 在服务重启或新版本部署后，预热可以帮助服务更快地恢复到正常运行状态。
 *
 * - **性能敏感型应用**：
 * 对于游戏服务器、实时数据处理等对延迟非常敏感的应用，预热是确保用户体验的重要步骤。
 *
 * 预热的具体实现方式可能包括模拟流量、执行测试脚本、预加载数据等。预热的策略和范围需要根据具体的应用场景和性能要求来定制。
 * @Date 22:40 2023-12-20
 **/

@Component
@Slf4j
public class ApplicationInit implements ApplicationListener<ApplicationReadyEvent> {

    Map<String, InitFunction> initFunctionMap = new HashMap<>();

    {
        initFunctionMap.put("预热fasterjson", this::initFastJson);
    }

    /**
     * 在服务器启动时需要预热什么服务，都可以在这里实现
     *
     * @param applicationReadyEvent
     */
    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        System.out.println("服务启动完成，开始预热服务");

        //  要进行预热的一系列服务，不应直接一直往下堆代码，可以定义一个map，将所有想执行函数全都抽取出来
        initFunctionMap.forEach((desc, function) -> {
            try {
                long start = System.currentTimeMillis();
                function.invoke();
                log.info("ApplicationInit{}.costTime{}", desc, System.currentTimeMillis() - start);
            } catch (Exception e) {
                log.error("ApplicationInit{}.error", desc, e);
            }
        });

    }

    private void initFastJson() {
        SkuDO skuDO = new SkuDO();
        skuDO.setSkuId(1L);
        skuDO.setSkuName("苹果");
        String s = JSON.toJSONString(skuDO);
        System.out.println(s);
        JSON.parseObject(s, SkuDO.class);
    }

    interface InitFunction {
        void invoke();
    }
}
